﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using WCell.Constants.World;
using WCell.Core.DBC;
using WCell.RealmServer;
using WCell.Util.Graphics;
using WCell.Util.Toolshed;
using WCell.MPQTool;
using NLog;

namespace WCell.Tools.Maps
{
	public static class RegionBoundaries
	{
		private static Logger log = LogManager.GetCurrentClassLogger();

		private static MpqManager manager;
		private const string baseDir = "WORLD\\maps\\";

		public static BoundingBox[] GetRegionBoundaries()
		{
			var wowRootDir = DBCTool.FindWowDir(null);
			if (!Directory.Exists(wowRootDir))
			{
				log.Error("Could not find WoW-directory: \"{0}\"", wowRootDir);
				return null;
			}

			manager = new MpqManager(wowRootDir);

			var boundaries = new BoundingBox[(int) MapId.End];
			var DBCMapEntryReader = new DBCReader<DBCMapEntry, DBCMapConverter>(
				RealmServerConfiguration.GetDBCFile("Map.dbc"));

			foreach (var dbcMapEntry in DBCMapEntryReader.EntryList)
			{
				var dir = dbcMapEntry.MapDirName;
				var wdtDir = Path.Combine(baseDir, dir);
				var wdtName = dir; 
                
				var wdt = WDTParser.Process(manager, wdtDir, wdtName);
			    if (wdt == null) continue;
                
				Vector3 min;
				Vector3 max;

				if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0)
				{
					const float maxDim = (1600.0f/3.0f)*(32.0f);
					
                    // No terrain, get the boundaries from the MODF
					min = wdt.ObjectDefinitions[0].Extents.Min;
					max = wdt.ObjectDefinitions[0].Extents.Max;

					var diff = Math.Max(max.X - min.X, max.Y - min.Y);
					if ((min.X + diff) > maxDim)
					{
						var newdiff = maxDim - diff;
						if ((min.X - newdiff) < -maxDim)
						{
							throw new Exception("Can't square this region.");
						}
						min.X = min.X - newdiff;
					}

					if ((min.Y + diff) > maxDim)
					{
						var newdiff = maxDim - diff;
						if ((min.Y - newdiff) < -maxDim)
						{
							throw new Exception("Can't square this region.");
						}
						min.Y = min.Y - newdiff;
					}

					max.X = min.X + diff;
					max.Y = min.Y + diff;
				}
				else
				{
					var minX = 64;
					var maxX = 0;
					var minY = 64;
					var maxY = 0;
					for (var y = 0; y < 64; y++)
					{
						for (var x = 0; x < 64; x++)
						{
							if (!wdt.TileProfile[x, y]) continue;
							minX = Math.Min(minX, x);
							maxX = Math.Max(maxX, x);
							minY = Math.Min(minY, y);
							maxY = Math.Max(maxY, y);
						}
					}

					var diff = Math.Max(maxX - minX, maxY - minY);
					if ((minX + diff) > 64)
					{
						var newdiff = 64 - diff;
						if ((minX - newdiff) < 0)
						{
							throw new Exception("Can't square this region.");
						}
						minX = minX - newdiff;
					}

					if ((minY + diff) > 64)
					{
						var newdiff = 64 - diff;
						if ((minY - newdiff) < 0)
						{
							throw new Exception("Can't square this region.");
						}
						minY = minY - newdiff;
					}

					maxX = minX + diff;
					maxY = minY + diff;

					var maxXLoc = (1600.0f/3.0f)*(32 - minX);
					var maxYLoc = (1600.0f/3.0f)*(32 - minY);
					var minXLoc = (1600.0f/3.0f)*(31 - maxX);
					var minYLoc = (1600.0f/3.0f)*(31 - maxY);

					min = new Vector3(minXLoc, minYLoc, -2048.0f);
					max = new Vector3(maxXLoc, maxYLoc, 2048.0f); 
				}

                // Switch into in-game coordinates
			    var temp = min.X;
			    min.X = min.Y;
			    min.Y = temp;

			    temp = max.X;
			    max.X = max.Y;
			    max.Y = temp;

				//text.WriteLine(dbcMapEntry.Id + "\t" + wdtName + "\tMin: " + min + "\tMax: " + max);
				boundaries[dbcMapEntry.Id] = new BoundingBox(min, max);
			}
			return boundaries;
		}
	}
}